#include<iostream>

#include<stdio.h>

#include<pthread.h>

#include<unistd.h>

#include<vector>

#include<string>

#include<queue>

#include<time.h>

#define N 10

#define Task task<int>

#define Block_queue block_queue<int>

using namespace std;

template<class T>
class block_queue;

template<class T>
struct task
{
    task(const string& str, block_queue<T>* const bq): _name(str), _bq(bq)
    {
    }

    string _name;
    pthread_t _PT;
    block_queue<T>* _bq;
    int _number;
};

template<class T>
class block_queue
{
public:
    block_queue()
    {
        high_water = (_max * 2) / 3;
        low_water = _max / 3;

        // for(int i = 0; i < _max; ++i)
        // {
        //     T n = rand() % 10; // 不泛型
        //     _qu.push(n);
        // }

        // cout << "high_water：" << high_water << endl << "low_water：" << low_water << endl;
    }

    int push(const T& x)
    {
        pthread_mutex_lock(&_MT);
        while(_qu.size() >= _max)
        {
            pthread_cond_wait(&_CD_PUSH, &_MT);
        }
        _qu.push(x);
        // if(_qu.size() >= high_water) pthread_cond_signal(&_CD_POP);
        // pthread_cond_signal(&_CD_POP);
        pthread_cond_broadcast(&_CD_POP);
        pthread_mutex_unlock(&_MT);
        return 0;
    }

    T pop()
    {
        pthread_mutex_lock(&_MT);
        while(_qu.size() == 0)
        {
            pthread_cond_wait(&_CD_POP, &_MT);
        }
        T back = _qu.front();
        _qu.pop();
        // if(_qu.size() <= low_water) pthread_cond_signal(&_CD_PUSH);
        // pthread_cond_signal(&_CD_PUSH);
        pthread_cond_broadcast(&_CD_PUSH);
        pthread_mutex_unlock(&_MT);
        return back;
    }

    void lock()
    {
        pthread_mutex_lock(&_MT);
    }

    void unlock()
    {
        pthread_mutex_unlock(&_MT);
    }

private:
    queue<T> _qu;
    int _max = N;
// public:
    static pthread_mutex_t _MT;
    static pthread_cond_t _CD_PUSH;
    static pthread_cond_t _CD_POP;

    int high_water;
    int low_water;
};

template<class T>
pthread_mutex_t block_queue<T>::_MT = PTHREAD_MUTEX_INITIALIZER;

template<class T>
pthread_cond_t block_queue<T>::_CD_PUSH = PTHREAD_COND_INITIALIZER;

template<class T>
pthread_cond_t block_queue<T>::_CD_POP = PTHREAD_COND_INITIALIZER;


pthread_mutex_t cout_mt = PTHREAD_MUTEX_INITIALIZER;

void* my_producer(void* arg)
{
    Task* tk = static_cast<Task*>(arg);
    while(1)
    {
        tk->_number = rand() % 10;

        sleep(1);
        tk->_bq->push(tk->_number);

        pthread_mutex_lock(&cout_mt);
        cout << tk->_name << "生产了：" << tk->_number << endl;
        pthread_mutex_unlock(&cout_mt);
    }
    return nullptr;
}

void* my_consumer(void* arg)
{
    Task* tk = static_cast<Task*>(arg);
    while(1)
    {
        sleep(1);
        int ret = tk->_bq->pop(); // 避免死锁
        pthread_mutex_lock(&cout_mt);
        cout << tk->_name << "消费掉：" << ret << endl;
        pthread_mutex_unlock(&cout_mt);
    }
    return nullptr;
}

int main()
{
    srand(time(nullptr));
    vector<Task*> arr;
    block_queue<int> BQ;

    for(int i = 0; i < 10; ++i)
    {
        Task* tmp = new Task("my_producer_thread", &BQ);
        tmp->_name += '0' + i;
        pthread_create(&(tmp->_PT), nullptr, my_producer, tmp);
        arr.push_back(tmp);
    }

    for(int i = 0; i < 10; ++i)
    {
        Task* tmp = new Task("my_consumer_thread", &BQ);
        tmp->_name += '0' + i;
        pthread_create(&(tmp->_PT), nullptr, my_consumer, tmp);
        arr.push_back(tmp);
    }

    for(auto& e : arr)
    {
        pthread_join(e->_PT, nullptr);
    }

    for(auto& e : arr)
    {
        delete e;
    }
    return 0;
}